#pragma once
 
#include <maya/MArgList.h>
#include <maya/MObject.h>
#include <maya/MGlobal.h>
#include <maya/MPxCommand.h>
#include <maya/MFloatPointArray.h>
#include <maya/MItDag.h>
#include <maya/MDagPath.h>
#include <maya/MFnMesh.h>
#include <maya/MFloatPoint.h>
#include <maya/MFloatVector.h>
#include <maya/MColor.h>
#include <maya/MFn.h>
#include "BBox.h"
#include "VoxelSpace.h"
#include "MeshInfo.h"
#include <limits>
#include <vector>
#include <fstream>
#include <iostream>
#include <map>

class Material;
extern const BBox BBoxSentinel;

struct RenderPlaneInfo
{
  RenderPlaneInfo()
    : ImgResX(0), ImgResY(0), LeftBottom(MPoint()), PixelHeight(0.0), PixelWidth(0.0),
    eyePoint(MPoint()), viewDirection(MVector(0,0,0)), upDirection(MVector(0,0,0)), rightDirection(MVector(0,0,0)),
    isOrtho(false), ImgPlaneH(0.0), ImgPlaneW(0.0), aspectRatio(1.0)
  {}

  int ImgResX;
  int ImgResY;
  MPoint LeftBottom;
  double PixelHeight;
  double PixelWidth;
  MFloatPoint eyePoint;
  MVector viewDirection;
  MVector upDirection;
  MVector rightDirection;
  bool isOrtho;
  double ImgPlaneH;
  double ImgPlaneW;
  double aspectRatio;
};


struct LightInfo
{
  MDagPath      light_path;
  MFn::Type     light_type;
  MFloatPoint   pos;
  MFloatVector  dir;
  MColor        color;
  float         intensity;
};

class RayTracer : public MPxCommand 
{
public:

  RayTracer() {};
  virtual MStatus   doIt(const MArgList& argList);
  int               intArg(const MArgList& args, unsigned int &indx, int & res);
  MStatus           parseArgs( const MArgList& args,
    int& width, int& height, int& voxelFactor, int& superSample, int& verbose);
  static void*      creator();
  bool              getCenterOfMass(const MDagPath& dagPath, bool quiet, MPoint& com);
  void              findHoles(MDagPath aPath, MFloatPointArray& vertexList, MIntArray& holeInfo, MIntArray& holeVertices, int& total_holes_vertices);
  bool              getBoundingBox(const MDagPath& dagPath, bool quiet, BBox& bbox_);
  bool              getBoundingBox(const std::vector<BBox>& bboxes, BBox& bbox_);
  bool				      intersectsTriangle(MFloatPoint& p0, MVector& r, MFloatPoint& p1, MFloatPoint& p2, MFloatPoint& p3, MFloatPoint& p, float& mult);
  MColor            calcColorAtPt(VoxelSpace vxspc, float uv1, float uv2, const MFloatPoint& vertPt, const MFloatVector& vertNorm, const MFloatPoint& eyePoint, const Material& material, const std::vector<LightInfo>& light_info_list, int& minMeshIdx, int& minHitFace, float& minHitBary1, float& minHitBary2);
  bool				      lightSeesPoint(VoxelSpace& vxspc, const MFloatPoint& light_pos, const MVector& light_vec, int mesh_idx, int face_idx, float bary1, float bary2, bool directional_light = false) const;

  MString			      buildOutputDirectoryName();

  void              triangulateMesh(const MFnMesh& mesh);
  void              triangulate_meshes();

  void              create_meshinfo_list(std::vector<MeshInfo>& mesh_info_vec);
  void              create_triangles_list(std::vector<std::vector<triangle>>& triangles_vector, int& triangle_count);
  /// gets bounding box of all meshes in the scene. optionally scales by scale_factor
  void              get_scene_bounding_box(BBox& scene_bb, float scale_factor=1.0);
  MStatus           get_renderplane_info(int width, int height, RenderPlaneInfo& rp_info);

  void              create_lightinfo_list(std::vector<LightInfo>& light_info_list);
};